package com.huayun.plugin.bk.service;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import javafx.scene.image.WritableImage;
import javax.imageio.ImageIO;
import javafx.application.Platform; // 导入Platform类

/**
 * AirfoilVisualizer 类，用于可视化翼型的压力分布。
 * 该类通过读取 XFOIL 生成的压力系数文件 (cp.txt)，绘制翼型的压力分布图，并提供鼠标交互功能以查看具体点的坐标和压力系数。
 */
public class AirfoilVisualizer extends Application {

    // 定义画布宽度和高度
    private static final int CANVAS_WIDTH = 1000;
    private static final int CANVAS_HEIGHT = 600;

    // 定义翼型缩放系数，用于将翼型数据映射到画布坐标系
    private static final double AIRFOIL_SCALE = 400;

    // 定义数据点显示半径，用于在画布上绘制翼型的每个点
    private static final double POINT_RADIUS = 2.0;

    // 存储翼型数据点的列表
    private List<AirfoilPoint> points = new ArrayList<>();

    // 存储压力系数的最小值和最大值，用于颜色映射
    private double cpMin = Double.MAX_VALUE;
    private double cpMax = Double.MIN_VALUE;

    // 信息标签，用于显示鼠标悬停时的坐标和压力系数
    private Label infoLabel = new Label("鼠标悬停查看坐标和Cp值");

    // 定义固定保存目录，用于存储生成的图像和数据文件
    private static final String SAVE_DIR = System.getProperty("user.dir") + File.separator + "xfoil";

    @Override
    public void start(Stage primaryStage) throws Exception {
        // 加载数据文件 cp.txt
        loadData(SAVE_DIR + File.separator + "cp.txt");

        // 计算压力系数范围，用于颜色映射
        calculateCpRange();

        // 创建画布和布局
        Canvas canvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        BorderPane root = new BorderPane(canvas);
        root.setPadding(new Insets(10));
        root.setBottom(infoLabel);

        // 绘制翼型压力分布
        drawAirfoil(gc);

        // 保存画布内容为图像文件
        saveCanvasImage(canvas);

        // 添加鼠标交互功能
        canvas.setOnMouseMoved(e -> {
            double mouseX = e.getX();
            double mouseY = e.getY();
            AirfoilPoint closest = findClosestPoint(mouseX, mouseY);
            updateInfoLabel(closest);
        });

        // 显示窗口（注释掉以避免直接运行）
//        Scene scene = new Scene(root);
//        primaryStage.setTitle("翼型压力分布可视化 - JavaFX");
//        primaryStage.setScene(scene);
//        primaryStage.show();
    }

    /**
     * 加载数据文件 cp.txt。
     * 该方法读取指定路径的文件，解析其中的翼型数据点，并将其存储到 points 列表中。
     *
     * @param filePath 数据文件路径
     * @throws Exception 如果文件读取或解析失败
     */
    private void loadData(String filePath) throws Exception {
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            String line;
            int lineNum = 0;
            while ((line = br.readLine()) != null) {
                lineNum= lineNum+ 1;
                if (lineNum < 4) continue;
                String[] parts = line.trim().split("\\s+");
                if (parts.length >= 3) {
                    double x = Double.parseDouble(parts[0]);
                    double y = Double.parseDouble(parts[1]);
                    double cp = Double.parseDouble(parts[2]);
                    points.add(new AirfoilPoint(x, y, cp));
                }
            }
        }
    }

    /**
     * 计算压力系数范围。
     * 该方法遍历所有数据点，找到最小和最大的压力系数值，用于后续的颜色映射。
     */
    private void calculateCpRange() {
        for (AirfoilPoint p : points) {
            if (p.cp < cpMin) cpMin = p.cp;
            if (p.cp > cpMax) cpMax = p.cp;
        }
    }

    /**
     * 根据压力系数生成颜色。
     * 该方法根据压力系数值在最小和最大值之间的比例，生成从蓝色到红色的渐变颜色。
     *
     * @param cp 压力系数值
     * @return 对应的颜色对象
     */
    private Color getColor(double cp) {
        double ratio = (cp - cpMin) / (cpMax - cpMin);
        return Color.color(ratio, 0, 1 - ratio); // R=ratio, B=1-ratio
    }

    /**
     * 绘制翼型及颜色映射。
     * 该方法在画布上绘制翼型的压力分布图，并添加颜色图例。
     *
     * @param gc 画布的 GraphicsContext 对象
     */
    private void drawAirfoil(GraphicsContext gc) {
        // 清空画布
        gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // 计算坐标偏移量（将翼型居中）
        double offsetX = CANVAS_WIDTH / 2 - AIRFOIL_SCALE / 2;
        double offsetY = CANVAS_HEIGHT / 2;

        // 绘制每个数据点
        for (AirfoilPoint p : points) {
            double x = offsetX + p.x * AIRFOIL_SCALE;
            double y = offsetY - p.y * AIRFOIL_SCALE; // Y轴向上为正
            gc.setFill(getColor(p.cp));
            gc.fillOval(x - POINT_RADIUS, y - POINT_RADIUS, 2 * POINT_RADIUS, 2 * POINT_RADIUS);
        }

        // 绘制颜色图例
        drawColorLegend(gc, offsetX + AIRFOIL_SCALE + 20, offsetY - AIRFOIL_SCALE / 2);
    }

    /**
     * 绘制颜色图例。
     * 该方法在画布上绘制颜色渐变条，并标注压力系数的最大值和最小值。
     *
     * @param gc 画布的 GraphicsContext 对象
     * @param x 图例的起始X坐标
     * @param y 图例的起始Y坐标
     */
    private void drawColorLegend(GraphicsContext gc, double x, double y) {
        double legendWidth = 30;
        double legendHeight = AIRFOIL_SCALE;

        // 渐变条
        for (int i = 0; i < legendHeight; i++) {
            double cp = cpMin + (cpMax - cpMin) * (1 - (double) i / legendHeight);
            gc.setFill(getColor(cp));
            gc.fillRect(x, y + i, legendWidth, 1);
        }

        // 标注数值
        gc.setFill(Color.BLACK);
        gc.fillText(String.format("%.2f", cpMax), x + legendWidth + 5, y);
        gc.fillText(String.format("%.2f", cpMin), x + legendWidth + 5, y + legendHeight);
    }

    /**
     * 查找距离鼠标最近的数据点。
     * 该方法根据鼠标位置，查找最接近的数据点。
     *
     * @param mouseX 鼠标X坐标
     * @param mouseY 鼠标Y坐标
     * @return 最接近的数据点，如果没有找到则返回 null
     */
    private AirfoilPoint findClosestPoint(double mouseX, double mouseY) {
        double offsetX = CANVAS_WIDTH / 2 - AIRFOIL_SCALE / 2;
        double offsetY = CANVAS_HEIGHT / 2;

        AirfoilPoint closest = null;
        double minDistance = Double.MAX_VALUE;

        for (AirfoilPoint p : points) {
            double x = offsetX + p.x * AIRFOIL_SCALE;
            double y = offsetY - p.y * AIRFOIL_SCALE;
            double distance = Math.sqrt(Math.pow(x - mouseX, 2) + Math.pow(y - mouseY, 2));
            if (distance < minDistance) {
                minDistance = distance;
                closest = p;
            }
        }
        return closest;
    }

    /**
     * 更新信息标签。
     * 该方法根据鼠标悬停的数据点，更新信息标签显示的内容。
     *
     * @param p 当前悬停的数据点
     */
    private void updateInfoLabel(AirfoilPoint p) {
        if (p != null) {
            infoLabel.setText(String.format("x/c=%.3f, y/c=%.3f, Cp=%.3f", p.x, p.y, p.cp));
        }
    }

    // 数据点结构
    private static class AirfoilPoint {
        double x, y, cp;

        public AirfoilPoint(double x, double y, double cp) {
            this.x = x;
            this.y = y;
            this.cp = cp;
        }
    }

    /**
     * 保存画布内容为图像文件。
     * 该方法将画布内容保存为 PNG 格式的图像文件。
     *
     * @param canvas 画布对象
     */
    private void saveCanvasImage(Canvas canvas) {
        try {
            // 创建保存目录（如果不存在）
            Files.createDirectories(Paths.get(SAVE_DIR));

            // 创建可写图像并捕获画布内容
            WritableImage writableImage = new WritableImage(CANVAS_WIDTH, CANVAS_HEIGHT);
            canvas.snapshot(null, writableImage);

            // 转换为BufferedImage并保存为PNG
            File outputFile = new File(SAVE_DIR +  File.separator +"cp.png");
            ImageIO.write(SwingFXUtils.fromFXImage(writableImage, null), "png", outputFile);

            System.out.println("图像已保存至: " + outputFile.getAbsolutePath());
//            Platform.exit();
        } catch (IOException e) {
            System.err.println("保存图像失败:");
            e.printStackTrace();
        }
    }

    /**
     * 主方法，启动 JavaFX 应用程序。
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        launch(args);
    }
}
